<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <title>猴子下山</title>
    <style>
        * {
            box-sizing: border-box;
        }

        body {
            padding: 0;
            margin: 0;
            font-family: sans-serif;
            background-color: #024f32;
            color: white;
        }

        .monkey {
            position: absolute;
            transition: 0.7s;
            width: 10px;
            height: 10px;
            transform: translateX(44px);
            --m: 1;
            --left-shoulder-angle: 40deg;
            --right-shoulder-angle: -170deg;
            --left-shoulder-pos: 0px;
            --right-shoulder-pos: calc(var(--m) * 30px);
            --left-elbow-angle: -50deg;
            --right-elbow-angle: -20deg;
            --waist-joint-angle: -45deg;
            --tail-angle: 12deg;
            --tail-base-angle: 40deg;
            --jump-height: 50px;
        }

        .monkey.reverse:not(.alternate) {
            --left-shoulder-angle: 10deg;
            --right-shoulder-angle: 140deg;
            --left-elbow-angle: 50deg;
            --right-elbow-angle: 30deg;
            --waist-joint-angle: 45deg;
            --tail-angle: -12deg;
            --tail-base-angle: -40deg;
        }

        .monkey.alternate {
            --left-shoulder-angle: -140deg;
            --right-shoulder-angle: -20deg;
            --left-elbow-angle: -30deg;
            --right-elbow-angle: -20deg;
        }

        .monkey.alternate.reverse {
            --left-shoulder-angle: 170deg;
            --right-shoulder-angle: -40deg;
            --left-elbow-angle: 20deg;
            --right-elbow-angle: 50deg;
            --waist-joint-angle: 45deg;
            --tail-angle: -12deg;
            --tail-base-angle: -40deg;
        }

        .swing-wrapper {
            width: 10px;
            height: 10px;
            animation: swing ease-in-out infinite 3s;
        }

        @keyframes swing {

            0%,
            100% {
                transform: rotate(-3deg);
            }

            50% {
                transform: rotate(3deg);
            }
        }

        .monkey * {
            position: absolute;
            transition: 0.7s;
            background-size: calc(var(--w) * var(--m)) calc(var(--h) * var(--m)) !important;
            width: calc(var(--w) * var(--m));
            height: calc(var(--h) * var(--m));
            background-repeat: no-repeat !important;
            image-rendering: pixelated;
        }

        .left {
            rotate: var(--left-shoulder-angle);
            translate: var(--left-shoulder-pos) 0;
            z-index: 5;
        }

        .reverse .left {
            z-index: -5;
        }

        .reverse .right {
            z-index: 5;
        }

        .right {
            rotate: var(--right-shoulder-angle);
            translate: var(--right-shoulder-pos) 0;
            z-index: -5;
        }

        .elbow {
            bottom: 0;
        }

        .left .elbow {
            rotate: var(--left-elbow-angle);
        }

        .right .elbow {
            rotate: var(--right-elbow-angle);
        }

        .waist>.joint {
            rotate: var(--waist-joint-angle);
        }

        .knee {
            bottom: 0;
            rotate: calc(var(--waist-joint-angle) * -2);
        }

        .body {
            --w: 30px;
            --h: 30px;
            background-image: url();
        }

        .head {
            top: calc(var(--m) * -10px);
            left: calc(var(--m) * 4px);
            --w: 22px;
            --h: 22px;
            background-image: url();
            z-index: 1;
        }

        .face {
            top: calc(var(--m) * 5px);
            left: calc(var(--m) * 4px);
            --w: 18px;
            --h: 14px;
            background-image: url();
        }

        .reverse .face {
            left: 0;
        }

        .shoulder {
            width: calc(100% + (calc(var(--m) * 10px)));
            top: 0;
            left: calc(var(--m) * -5px);
            height: calc(var(--m) * 10px);
        }

        .waist {
            height: calc(var(--m) * 10px);
            bottom: 0;
            width: 100%;
        }

        .right-leg {
            right: 0;
        }

        .thigh,
        .leg {
            background-image: url();
            --w: 10px;
            --h: 20px;
        }

        .joint {
            width: calc(var(--m) * 10px);
            height: calc(var(--m) * 10px);
        }

        .arm {
            background-image: url();
            --w: 10px;
            --h: 25px;
        }

        .monkey.jump .body {
            animation: jump-up forwards 0.35s;
        }

        @keyframes jump-up {

            0%,
            100% {
                translate: 0;
            }

            70% {
                translate: 0 calc(var(--m) * var(--jump-height));
            }
        }

        .monkey .body {
            top: calc(var(--m) * 30px);
            left: calc(var(--m) * -28px);
        }

        .monkey.alternate .body {
            top: calc(var(--m) * 28px);
            left: calc(var(--m) * -10px);
        }

        .monkey.reverse .body {
            top: calc(var(--m) * 28px);
            left: calc(var(--m) * -15px);
        }

        .monkey.reverse.alternate .body {
            top: calc(var(--m) * 30px);
            left: calc(var(--m) * 0px);
        }

        .outer-handle-wrapper {
            position: absolute;
            width: 100%;
            display: flex;
            justify-content: space-evenly;
            max-width: 1000px;
            left: 50%;
            transform: translateX(-50%);
        }

        .outer-handle-wrapper:last-of-type {
            padding-bottom: 440px;
        }

        .tail-joint {
            width: calc(4px * var(--m));
            height: calc(4px * var(--m));
            bottom: 0;
            left: 2px;
            transform: rotate(var(--tail-base-angle));
        }

        .tail-joint .joint {
            width: calc(4px * var(--m));
            height: calc(4px * var(--m));
            bottom: 0;
            transform: rotate(var(--tail-angle));
        }

        .reverse .tail-joint {
            left: calc(28px * var(--m));
        }

        .tail {
            width: calc(4px * var(--m));
            height: calc(12px * var(--m));
            background-color: #b08536;
        }

        .outer-handle-wrapper:nth-child(odd) {
            flex-direction: row-reverse;
        }

        .handle-wrapper {
            width: 20px;
            height: 100%;
        }

        .handle-wrapper:nth-child(2) {
            padding-top: 20px;
        }

        .handle-wrapper:nth-child(3) {
            padding-top: 40px;
        }

        .handle-wrapper:nth-child(4) {
            padding-top: 60px;
        }

        .handle-wrapper:nth-child(5) {
            padding-top: 80px;
        }

        .handle {
            background-image: url();
            width: 20px;
            height: 20px;
            background-size: 20px;
            background-repeat: no-repeat !important;
            image-rendering: pixelated;
        }

        .text-wrapper {
            position: absolute;
            top: 0;
            width: 100%;
            margin: 80px auto 0;
        }

        p {
            text-align: center;
            color: #b08536;
            font-family: 'Courier New', Courier, monospace;
            font-size: 1.3rem;
        }

        small {
            font-size: 0.95rem;
        }

        .sign {
            position: fixed;
            font-family: Arial, Helvetica, sans-serif;
            color: #b08536;
            bottom: 10px;
            right: 10px;
            font-size: 0.6rem;
            text-transform: none;
        }

        a {
            color: #b08536;
            text-decoration: none;
            text-transform: none;
        }

        a:hover {
            text-decoration: underline;
        }
    </style>
</head>

<body>
    <div class="text-wrapper">
        <p>滚动界面</p>
        <p><small>不要滚太快，猴子下山得要点时间</small></p>
    </div>
    <div class="wrapper">
        <div class="monkey alternate">
            <div class="swing-wrapper">
                <div class="body">
                    <div class="head">
                        <div class="face"></div>
                    </div>
                    <div class="shoulder">
                        <div class="joint left">
                            <div class="arm">
                                <div class="joint"></div>
                                <div class="joint elbow">
                                    <div class="arm"></div>
                                </div>
                            </div>
                        </div>
                        <div class="joint right">
                            <div class="arm">
                                <div class="joint"></div>
                                <div class="joint elbow">
                                    <div class="arm"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="waist">
                        <div class="joint">
                            <div class="thigh">
                                <div class="joint knee">
                                    <div class="leg"></div>
                                </div>
                            </div>
                        </div>
                        <div class="tail-joint">
                            <div class="tail">
                                <div class="joint">
                                    <div class="tail">
                                        <div class="joint">
                                            <div class="tail">
                                                <div class="joint">
                                                    <div class="tail"></div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="joint right-leg">
                            <div class="thigh">
                                <div class="joint knee">
                                    <div class="leg"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script>function init() { const settings = { handlePos: null, index: 0, nearestIndex: 0, }; const monkey = { el: document.querySelector('.monkey'), x: 44, y: 0, alternate: false, }; const wrapper = document.querySelector('.wrapper'); const setStyles = ({ el, x, y, deg }) => { el.style.transform = `translate(${x ? `${x}px` : 0},${y ? `${y}px` : 0})rotate(${deg || 0}deg)` }; const createHandles = (y, n) => { const handles = { el: Object.assign(document.createElement('div'), { className: 'outer-handle-wrapper', innerHTML: new Array(n).fill('').map(() => { return `<div class="handle-wrapper"><div class="handle"></div></div>` }).join(''), }), }; handles.el.style.top = `${y}px`; wrapper.appendChild(handles.el) };[[300, 5], [150, 3], [150, 4], [150, 2], [150, 4], [100, 2], [100, 5], [150, 2], [150, 4], [150, 3], [100, 2], [100, 5],].map((itemA, indexA, arr) => { return [arr.map((itemB, indexB) => { const prevHandlesHeight = indexB === 0 ? 0 : arr[indexB - 1][1] * 20; return indexB <= indexA ? itemB[0] + prevHandlesHeight : 0 }).reduce((n, acc) => { return n + acc }, 0), itemA[1],] }).forEach((itemC) => createHandles(itemC[0], itemC[1])); const handles = document.querySelectorAll('.handle'); const monkeyJump = ({ x, y }) => { monkey.el.classList[monkey.x > x ? 'add' : 'remove']('reverse'); monkey.el.classList[monkey.alternate ? 'add' : 'remove']('alternate'); monkey.alternate = !monkey.alternate; monkey.x = x; monkey.el.style.setProperty('--jump-height', monkey.y - y > 80 ? `${y - monkey.y - 10}px` : '-50px'); monkey.y = y; setStyles(monkey); monkey.el.classList.add('jump'); setTimeout(() => { monkey.el.classList.remove('jump') }, 500) }; const getHandlePos = () => { return [...handles].map((handle) => { const { x, y } = handle.getBoundingClientRect(); return { x: x + window.scrollX, y: y + window.scrollY, } }) }; settings.handlePos = getHandlePos(); window.addEventListener('resize', () => { settings.handlePos = getHandlePos(); const { x, y } = settings.handlePos[settings.index]; monkeyJump({ x, y }) }); const getNearestHandleIndex = () => { const y = Math.round(window.scrollY + window.innerHeight / 2); const nearestIndex = settings.handlePos.map((handle, i) => { return { i, diff: Math.abs(handle.y - y), } }).sort((a, b) => a.diff - b.diff)[0].i; settings.nearestIndex = nearestIndex }; setInterval(() => { getNearestHandleIndex(); if (settings.index === settings.nearestIndex) return; const { x, y } = settings.handlePos[settings.index]; monkeyJump({ x: x + 5, y: y + 5, }); if (settings.index > settings.nearestIndex && settings.index >= 0) { settings.index-- } else if (settings.index < settings.nearestIndex && settings.index < handles.length - 1) { settings.index++ } }, 700) } window.addEventListener('DOMContentLoaded', init);</script>
</body>

</html>